module jinji.common.query;

public import jinji.config;
public import jinji.common.type,
jinji.rpc,
jinji.util.query,
jinji.util.string;
public import jinji.db : SB;
public import tame.builtins;


version (Mock) {
} else {
	public import dast.wsrpc;

	alias Request = WSRequest;
}
/// Query Result
enum QR(alias T) = QR!(T.stringof);
/// ditto
enum QR(string s) = type("[count: number, ...items: " ~ s ~ "[]]");

template getObject(T, string name, string desc,
	string returns, string defaultOrder = "") {

	mixin(`@summary(' ' ~ desc) @returns @QR!T @Action void `, name,
		q{(ref Request req, @"筛选条件"
@type("Filter[], order?: string, offset?: number, limit?: number") Filter[] filters = []) {
	string order = defaultOrder;
	req.read(order);
	if (!order.length)
		order = defaultOrder;
	int offset = req.read(0);
	int limit = req.read(20);
	req.queryAll!T(filters, order, offset, limit);
}});
}

/// 计数
enum count(T, string target = "*") = SB.select!("count(" ~ target ~ ")")
		.from!T;

// 查询语句解析

auto whereIdIn(SB sb, in long[] ids) {
	auto r = fixedBuf!(1024, "id列表过长");
	r ~= "id in (";
	foreach (i, id; ids) {
		if (i)
			r ~= ',';
		r.write(id);
	}
	r ~= ')';
	return sb.where(r.data);
}

/// 排序
auto orderByCE(SB sb, in char[] order) {
	auto buf = fixedBuf!(1024, "排序表达式过长");
	return sb.orderBy(parseCE(order, buf));
}

/// 条件查询
void queryAll(T)(ref Request req, Filter[] filters,
	string order, int offset, int limit) {
	import jinji.util.io;

	enum sb = SB.select!T;
	enum cnt = count!T;
	if (!filters.length) {
		long c = query!cnt.get!long;
		req.send(c);
		if (c && limit)
			req.findAll!(T, sb)(order, offset, limit);
		return;
	}

	checkArg(filters.stringof, filters.length <= C.maxFilters, "查询条件过多");
	auto buf = fixedBuf!(1024, "查询条件过长");
	parseFilters(filters, buf);
	const where = buf.data;
	long c = void;
	{
		const sql = cnt.where(where);
		auto q = query(sql);
		setFilters(q, filters, buf);
		c = q.get!long;
		req.send(c);
	}
	if (!c || !limit)
		return;

	char[11] p = void;
	p[0] = '$';
	const p1 = p[0 .. intToStr(&p[1], filters.length + 1) + 1];
	auto s = sb.where(where).orderByCE(order)
		.limit(p1);
	const p2 = p[0 .. intToStr(&p[1], filters.length + 2) + 1];
	auto q = query(s.offset(p2));
	setFilters(q, filters, buf);
	q.set(limit, offset);
	foreach (row; q)
		req.sendObj(row.get!T);
}

private:
void findAll(T, SB sb, A...)(ref Request req, string order, int offset, int limit, A args) {
	import std.algorithm : count;
	import tame.conv : text;

	enum index = sb.sql.count('$');
	enum p1 = text('$', index + 1);
	enum p2 = text('$', index + 2);
	foreach (row; query(sb.orderByCE(order)
			.limit(p1).offset(p2), args, limit, offset))
		req.sendObj(row.get!T);
}

void setFilters(S)(ref Query q, in Filter[] filters, ref scope S buf) {
	foreach (f; filters) {
		with (FilterOp) {
			if (f.op != custom && f.op != isNull && f.op != isNotNull) {
				if (f.op == like || f.op == notLike) {
					const p = buf.pos;
					buf ~= '%';
					escapeValue(f.value, buf);
					buf ~= '%';
					q.set(buf[p .. buf.pos]);
				} else
					q.set(f.value);
			}
		}
	}
}

public:

/// 添加/修改对象，若第i个参数为空则删除对象
pragma(inline, true) void modify(T, size_t i = 1, A...)(ref Request req, auto ref A args)
if (T.tupleof.length > i) {
	import std.traits;

	enum field = ColumnName!(T, __traits(identifier, T.tupleof[0]));
	static if (is(OriginalType!(A[i]) == U[], U))
		const update = args[i].length > 0;
	else
		const update = cast(bool)args[i];
	if (update) {
		// 插入或更新
		// 插入时忽略自增id、创建时间、更新时间
		// 更新时忽略创建时间
		/+static if (is(typeof(() { T t = T(args); })))
		const time = now;
		req.send(exec!(SB.insert(T, skipOnCreate) ~ " ON CONFLICT DO " ~
				SB.update("")
				.set(`updated_at=now()`)
				.where("name=$1"))(name, req.uid));+/
		req.send(db.replaceInto(T(args)));
	} else
		req.send(db.delWhere!(T, field ~ "=$1")(args[0]));
}
